/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.taglib.core.content;

import com.blandware.atleap.webapp.struts.ContentTilesRequestProcessor;
import com.blandware.atleap.webapp.taglib.core.util.ContextMenuItem;
import com.blandware.atleap.webapp.taglib.core.util.JavaScriptUtil;
import com.blandware.atleap.webapp.util.core.ApplicationResources;
import com.blandware.atleap.webapp.util.core.RequestUtil;
import com.blandware.atleap.webapp.util.core.WebappConstants;
import com.blandware.atleap.webapp.util.core.WebappUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.validator.GenericValidator;
import org.apache.struts.taglib.TagUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.Map;

/**
 * <p>While in edit mode this tag wraps body with &lt;div&gt;&lt;/div&gt; tags to mark it editable.
 * When system is in edit mode and user double-clicks on editable area, action with rewritten URI is called.
 * Also generated DIV is supplied with context menu; user may do following using
 * it: edit for current content locale (the same as if user double-clicked it),
 * edit for all content locales, or edit for selected content locales.
 * Technically, the only difference between URIs, corresponding to those three
 * context menu items, is that for first case (editing for current locale)
 * <code>localesMode=current</code> is added to URI, and for second case (editing
 * for all locales) <code>localesMode=all</code> is added.
 * </p>
 * <p>
 * If nothing is supplied by tag body, or result is empty string, then message
 * like "The field value is empty, please insert the text" is treated as result.
 * </p>
 * <p>
 * Allowed parameters are:
 * <ul>
 * <li>
 * <b>action</b> - context-relative URI of Struts Action to call to update
 * content which is placed inside this tag
 * </li>
 * <li>
 * <b>forward</b> - name of global Action Forward to use to update content which
 * is placed inside this tag
 * </li>
 * <li>
 * <b>uri</b> - context-relative URI of action to call to update content which
 * is placed inside this tag
 * </li>
 * <li>
 * <b>contextMenuJsp</b> - path to JSP which will render a context menu. If not
 * given, <code>WebappConstants.DEFAULT_CONTEXT_MENU_JSP</code> is used.
 * </li>
 * </ul>
 * </p>
 * <p>
 * At least one of <b>action</b>, <b>forward</b> or <b>uri</b> must be
 * specified. When calculating URI, firstly <b>action</b> is considered, then
 * (if it's <code>null</code>), <b>forward</b> is taken into account. If it's
 * <code>null</code> too, <b>uri</b> is used.
 * </p>
 * <p><a href="EditTag.java.html"><i>View Source</i></a></p>
 * <p/>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.22 $ $Date: 2007/04/03 19:27:25 $
 * @see com.blandware.atleap.webapp.taglib.core.content.EditCollectionTag
 * @jsp.tag name="edit"
 * body-content="scriptless"
 */
public class EditTag extends SimpleTagSupport {

	protected transient final Log log = LogFactory.getLog(EditTag.class);
	protected static final String NUMBER_KEY = "com.blandware.atleap.taglib.content.EDIT_TAG_NUMBER";

	/**
	 * Context-relative URI of Struts Action to call to update content which is placed inside this tag
	 */
	protected String action = null;

	/**
	 * Name of global ActionForward to use to update content which is placed inside this tag
	 */
	protected String forward = null;

	/**
	 * Context-relative URI of action to call to update content which is placed inside this tag
	 */
	protected String uri = null;

	/**
	 * Path to JSP which will render a context menu
	 */
	protected String contextMenuJsp = null;


	/**
     * Returns Struts Action
     *
     * @return Struts Action
     * @see #action
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Context-relative URI of Struts Action to call to update content which is placed inside this tag"
	 */
	public String getAction() {
		return action;
	}

    /**
     * Sets Struts Action
     *
     * @param action Struts Action to set
     * @see #action
     */
	public void setAction(String action) {
		this.action = action;
	}

	/**
     * Returns ActionForward
     *
     * @return ActionForward
     * @see #forward
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Name of global ActionForward to use to update content which is placed inside this tag"
	 */
	public String getForward() {
		return forward;
	}

    /**
     * Sets ActionForward
     *
     * @param forward ActionForward to set
     * @see #forward
     */
	public void setForward(String forward) {
		this.forward = forward;
	}

	/**
     * Returns URI
     *
     * @return URI
     * @see #uri
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Context-relative URI to call to update content which is placed inside this tag"
	 */
	public String getUri() {
		return uri;
	}

    /**
     * Sets URI
     *
     * @param uri URI to set
     * @see #uri
     */
	public void setUri(String uri) {
		this.uri = uri;
	}

	/**
     * Returns address of JSP that will be used to render a context menu
     *
	 * @return address of JSP for context menu
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Path to JSP which will render a context menu"
	 */
	public String getContextMenuJsp() {
		return contextMenuJsp;
	}

    /**
     * Sets address of JSP that will be used to render a context menu
     *
     * @param contextMenuJsp address of JSP for context menu to set
     */
	public void setContextMenuJsp(String contextMenuJsp) {
		this.contextMenuJsp = contextMenuJsp;
	}

    /**
     * Processes the tag
     *
     * @throws JspException
     * @throws IOException
     */
	public void doTag() throws JspException, IOException {

		PageContext pageContext = (PageContext) getJspContext();
		HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
		ApplicationResources applicationResources = ApplicationResources.getInstance(pageContext.getServletContext());

		if ( uri == null && action == null && forward == null ) {
			JspTagException e = new JspTagException("One of the attributes 'action', 'forward' or 'uri' must be specified");
			throw e;
		}

		if ( action != null ) {
			uri = WebappUtil.getActionMappingURL(action, null, request, WebappConstants.URL_TYPE_DOMAIN_RELATIVE);
		} else if ( forward != null ) {
			try {
				uri = WebappUtil.getActionForwardURL(forward, null, request, WebappConstants.URL_TYPE_DOMAIN_RELATIVE);
			} catch ( MalformedURLException e ) {
				JspTagException ex = new JspTagException(e);
				throw ex;
			}
		}

		// calculate tag number
		Integer tagNumber = (Integer) pageContext.getAttribute(NUMBER_KEY, PageContext.REQUEST_SCOPE);
		if ( tagNumber == null ) {
			tagNumber = new Integer(0);
		} else {
			tagNumber = new Integer(tagNumber.intValue() + 1);
		}

		pageContext.setAttribute(NUMBER_KEY, tagNumber, PageContext.REQUEST_SCOPE);


		String content = null;
		boolean contentExists = true;
		StringWriter sw = new StringWriter();
		JspFragment body = getJspBody();
		if ( body != null ) {
			getJspBody().invoke(sw);
			content = sw.toString();
			contentExists = content.trim().length() > 0;
		} else {
			contentExists = false;
		}

		TagUtils tagUtils = TagUtils.getInstance();
		if ( !contentExists ) {
			content = applicationResources.getMessage(request, "core.commons.contentTag.insertText");
		}

		if ( GenericValidator.isBlankOrNull(contextMenuJsp) ) {
			contextMenuJsp = WebappConstants.DEFAULT_CONTEXT_MENU_JSP;
		}

		Object editModeEnabled = request.getSession().getAttribute(WebappConstants.SITE_EDIT_MODE_ENABLED_KEY);
		boolean createWrapper = editModeEnabled != null && Boolean.TRUE.equals(editModeEnabled);
		if ( createWrapper ) {
			StringBuffer divId = new StringBuffer("__editable__content__").append(tagNumber).append("__wrapper__");
			String requestUrl = (String) request.getAttribute(ContentTilesRequestProcessor.PROCESSED_URL);

			// make context menu ID
			StringBuffer contextMenuId = new StringBuffer("__contextMenu__edit__").append(tagNumber.toString());
			// make URIs for context menu
			Map params = RequestUtil.getRequestParametersFromUri(uri);
			String base = RequestUtil.getBaseFromUri(uri);
			params.put("localesMode", "current");
			String uriForCurrentLocale = RequestUtil.appendParams(base, params);
			params.put("localesMode", "all");
			String uriForAllLocales = RequestUtil.appendParams(base, params);
			String uriForChosenLocales = uri;

			/*
			   That's a name of JS variable that will hold this
			   context menu (that variable is used to refer to this context
			   menu from showContextMenu(), for instance)
			*/
			StringBuffer contextMenuLayerVar = new StringBuffer(contextMenuId.toString());

			StringBuffer onmouseover = new StringBuffer("doSelectLayer(this.id);");
			StringBuffer onmouseout = new StringBuffer("doUnselectLayer(this.id);");
			StringBuffer ondblclick = new StringBuffer("doCallContentUpdate('").append(uriForCurrentLocale).append("', '").append(requestUrl).append("');");
			StringBuffer oncontextmenu = new StringBuffer("return showContextMenu(").append(contextMenuLayerVar).append(", event);");

			String divDisplay = "block";

			// append content to wrapper and close DIV
			StringBuffer wrappedContent = new StringBuffer("<div id=\"").append(divId)
			        .append("\" name=\"").append(divId)
			        .append("\" class=\"fieldValueWrapper\" style=\"display: ")
			        .append(divDisplay).append(";\" ").append("onmouseover=\"")
			        .append(onmouseover).append("\" onmouseout=\"").append(onmouseout)
			        .append("\" ondblclick=\"").append(ondblclick)
			        .append("\" oncontextmenu=\"").append(oncontextmenu).append("\">")
			        .append(content).append("</div>");

			tagUtils.write(pageContext, wrappedContent.toString());

			// Insert context menu
			// Obtain titles
			String editInCurrentLocaleTitle = applicationResources.getMessage(request,
                    "core.editMode.menu.context.editInCurrentLocale");
			String editInAllLocalesTitle = applicationResources.getMessage(request,
                    "core.editMode.menu.context.editInAllLocales");
			String editInChosenLocalesTitle = applicationResources.getMessage(request,
                    "core.editMode.menu.context.editInChosenLocales");

            String normalClass = WebappConstants.CONTEXT_MENU_ITEM_STYLE_CLASS;
            String hltdClass
                    = WebappConstants.HIGHLIGHTED_CONTEXT_MENU_ITEM_STYLE_CLASS;
            
            // Create and populate list of items
			ContextMenuItem menu = new ContextMenuItem();
			menu.setId(contextMenuId.toString());
            menu.addChildItem(new ContextMenuItem(JavaScriptUtil.createLinkAsLayer("javascript:doCallContentUpdate('" + uriForCurrentLocale + "', '" + requestUrl + "');",
			        editInCurrentLocaleTitle, normalClass, hltdClass)));
			menu.addChildItem(new ContextMenuItem(JavaScriptUtil.createLinkAsLayer("javascript:doCallContentUpdate('" + uriForAllLocales + "', '" + requestUrl + "');",
			        editInAllLocalesTitle, normalClass, hltdClass)));
			menu.addChildItem(new ContextMenuItem(JavaScriptUtil.createLinkAsLayer("javascript:doCallContentUpdate('" + uriForChosenLocales + "', '" + requestUrl + "');",
			        editInChosenLocalesTitle, normalClass, hltdClass)));

			// Store menu in request
			pageContext.setAttribute(WebappConstants.CONTEXT_MENUS_KEY, Arrays.asList(new ContextMenuItem[]{menu}), PageContext.REQUEST_SCOPE);

			// Do include
			try {
				pageContext.include(contextMenuJsp);
			} catch ( ServletException ex ) {
				JspTagException e = new JspTagException(ex);
				TagUtils.getInstance().saveException(pageContext, e);
				throw e;
			}


		} else {
			tagUtils.write(pageContext, content);
		}

	}

}
